<?php
/*
* boot.php
* yc@2009-09-11 09:55:40
*/

error_reporting(E_ALL);
set_error_handler('my_error_handler');
set_exception_handler('my_exception_handler');

require '../app/config.php';

// handle session
session_save_path($ENV['root'] . '/app/tmp');
ini_set('session.name', $ENV['sess.name']);
ini_set('session.gc_maxlifetime', $ENV['sess.gcinterval']);
ini_set('session.use_cookies', 1); 
ini_set('session.use_only_cookies', 1); //PHPSESSID in url not allowed
ini_set('session.cookie_lifetime', 0); //close seesion when all browser closed
ini_set('session.cookie_httponly', 1); //defend XSS
ini_set('date.timezone','Asia/Shanghai');
session_start();
/*
* _() function 
*/
if (!function_exists('_')){
    function _($str)
    {
        return $str;
    }
}
run();

function my_error_handler($errno, $str, $file, $line)
{
    echo $str . "<br /><br /><p>Stoped at <strong>{$file}:{$line}</strong><br /></p>";
    debug();
}
function my_exception_handler($e)
{
    echo '<p>Exception: <strong>'.$e->getMessage().'</strong><br /></p>';
    debug();
}


/*
* for logged-in user,check login every $ENV['login.life'] seconds, this does not contain the AUTH function
* keep session. when out of login.life, force logout user
*/
function keepLogin()
{
    global $ENV;
    // ignore guests
    if (!isset($_SESSION['login.lastActive']) || !$ENV['app.auth'])
        return;
    if ($ENV['time'] - $_SESSION['login.lastActive'] > $ENV['login.life']){
        unset($_SESSION['login.lastActive']);
        $auth = loadModel('Auth');
        $auth->logout();
    }
    //update last active time
    $_SESSION['login.lastActive'] = $ENV['time'];
}
function run()
{
    global $ENV;

    keepLogin();
    route();
    $c = loadController($ENV['controller']);
    if ($c === false)
        debug();

    //filter hidden actions
    filterAction($c, $ENV['action']);
    debug('request action: <strong>' . $ENV['action'] . '</strong>');

    //load model class
    if (isset($c->models))
        foreach (explode(',', $c->models) as $m)
                $c->$m = loadModel($m);
    $auth = loadModel('Auth');
    if(!$auth->isLogin() && $ENV['action'] != 'login')
        redirect('index', 'login');
    //TODO add chain calling
    call_user_func(array($c, $ENV['action']));
}

/*
*
*
*/
function debug($str = FALSE)
{
    static $logs = array();
    // print out logs or not
    if ($str === FALSE){
        echo '<p style="font-size:12px;font-weight:bold;">Debug info:<ol style="font-size:12px;font-family:;sans-serif;margin:0;">';
        foreach ($logs as $l)
            echo '<li>' . $l . '</li>';
        echo '</ol></p>';
        die();
    }
    $logs[] = $str;
    return true;
}
/*
* get a instance of a model, singleton
* 
* @param string $model class name of the model
* @return object|false
*/
function loadModel($model)
{
    global $ENV;
    static $models = array();
    if (!array_key_exists($model, $models)){
        $f = $ENV['root'] . '/app/model/' . $model . '.php';
        if (!is_file($f)){
            debug('model <strong>'. $model . '</strong> not exist.');
            return false;
        }
        if (count($models) == 0)
            require_once($ENV['root'] . '/app/model/Model.php');
        require_once($f);
        $models[$model] = new $model;
        debug('model <strong>'. $model . '</strong> loaded.');
    }
    return $models[$model];
}

/*
*
* see loadModel
*/
function loadController($name)
{
    global $ENV;
    static $controllers= array();
    if (!array_key_exists($name, $controllers)){
        $f = $ENV['root'] . '/app/controller/' . $name. '.php';
        if (!is_file($f)){
            debug('controller <strong>' . $name . '</strong> not exist.');
            return false;
        }
        if (count($controllers) == 0)//first time, init parent class
            require_once($ENV['root'] . '/app/controller/Controller.php');
        require_once($f);
        $name .= 'Controller';
        $controllers[$name] = new $name;
        debug('controller <strong>' . $name . '</strong> loaded.');
    }
    return $controllers[$name];
}

/*
*
* see loadModel
*/
function loadLib($name)
{
    global $ENV;
    static $libs= array();
    if (!array_key_exists($name, $libs)){
        $f = $ENV['root'] . '/lib/' . $name. '.php';
        if (!is_file($f)){
            debug('lib <strong>' . $name . '</strong> not exist.');
            return false;
        }
        if (count($libs) == 0)//
            require_once($ENV['root']. '/lib/Db.php');
        require_once($f);
        $libs[$name] = new $name;
        debug('lib <strong>' . $name . '</strong> loaded.');
    }
    return $libs[$name];
}

/*
* filter hidden actions from called controller
*
* @param object $obj instance of XXController
* @param string $method called method
*/
function filterAction($obj, &$method)
{
//    //优先xxAction样子的action
//    $assumeAction = $method . 'Action';
//    if (method_exists($obj, $assumeAction))
//        $method = $assumeAction;
//    //其次才是一般的action
    if (!method_exists($obj, $method)){
        if(method_exists($obj, 'index')){
            $method = 'index';
        } else {
            debug('method <strong>' . $method . '</strong> not exist.');
            debug();
        }
    }
    if (!isset($obj->hiddenActions))
        return;
    if (in_array($method, explode(',', $obj->hiddenActions)))
        $method = 'index';
}
function route()
{
    global $ENV, $PARAM;
    //parse url
    $uri = urldecode(clearUrl($_SERVER['REQUEST_URI']));
    $xss = array('<', '>', '%3c', '%3e');
    $uri = str_replace($xss, '', $uri);
    debug('request uri: <strong>' . $uri . '</strong>');
    $uriArr = explode('/', $uri);

    //get controller and action
    !empty($uriArr[0]) && ($ENV['controller'] = $uriArr[0]);
    isset($uriArr[1]) && ($ENV['action'] = $uriArr[1]);
    $ENV['controller'] = ucfirst(strtolower($ENV['controller']));
    $ENV['action'] = strtolower($ENV['action']);
    debug('request controller: <strong>' . $ENV['controller'] . '</strong>');

    //uri: /index/someAction/param1/value1/param2/value2/....
    $p = 2;
    $par = array();
    while (isset($uriArr[$p])){
        $par[$uriArr[$p]] = isset($uriArr[$p+1]) ? $uriArr[$p+1] : '';
        $p += 2;
    }
    debug('request params: <strong>' . http_build_query($par) . '</strong>');
    //set params
    $PARAM = array_merge($_POST, $_GET, $par);
    unset($_GET, $_POST);
}

/*
* trim url
*
* @param  string $url like '/index/a///'
* @return string clean url like 'index/a'
*/
function clearUrl($url)
{
    $new = str_replace('//', '/', $url);
    if ($new != $url)
        return clearUrl($new);
    return trim($new, ' /');
}    

/*
* redirect browser by $controller and $action and $param
*/
function redirect($controller, $action = null, $param = null)
{
    is_null($action) && ($action = 'index');
    $url = getUrl($controller, $action, $param);
    header('Location: ' . $url);
    die();
}

function getCurUrl()
{
    global $PARAM;
    return getUrl(null, null, $PARAM);
}

function getUrl($controller = null, $action = null, $param = null)
{
    global $ENV;
    is_null($action) && ($action = strtolower($ENV['action']));
    empty($param) && ($action == 'index') && ($action = '');
    is_null($controller) && ($controller = strtolower($ENV['controller']));
    //$port = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':' . $_SERVER['SERVER_PORT']; # fuck SERVER_PORT
    //$url = 'http://' . $_SERVER['SERVER_NAME'] . $port . '/' . $controller . '/' . $action;
    $url = 'http://' . $_SERVER['HTTP_HOST'] . '/' . $controller . '/' . $action;
    if (is_array($param))
        foreach($param as $k => $v)
            $url .= '/' . htmlspecialchars($k) . '/' . htmlspecialchars($v);
    return $url;
}

//$data must be explict type
function quoteData(&$data = array())
{
    if (!$data || empty($data))
        return array();
    foreach ($data as $key => $val){
        if (!is_int($val))//string
            $data[$key] = '\'' . addslashes($val) . '\'';
    }
}

function clearSlashes($data = null)
{
    $toReplace = array('"', '\'', '\\', '/');
    if (is_array($data)){
        foreach ($data as $k => $v)
            $data[$k] = str_replace($toReplace, '', $v);
        return $data;
    } else
        return $data ? str_replace($toReplace, '', $data) : '';
}

function buildQuery($data = array())
{
    $ret = '';
    foreach ($data as $k => $v)
        $ret .= sprintf('/%s/%s', $k, $v);
    return $ret;
}

function friendlyDate($date, $now = null)
{
    global $ENV;
    is_null($now) && ($now = $ENV['time']);
    if (!is_numeric($date) or !is_numeric($now))
        return '';
    $seconds = $now - $date;
    if ($seconds < 0)
        debug('time error') && debug();
    $days = (int)($seconds / 86400);
    $hours = (int)($seconds % 86400 / 3600);
    $minutes = (int)($seconds % 86400 % 3600 / 60);
    if ($days)
        return $days >= 30 ? floor($days / 30) . _('个月前') : $days . _('天前');
    if ($hours)
        return $hours . _('小时前');
    if ($minutes)
        return $minutes . _('分钟前');
    return _('几秒前');
}

function arrayTrim($arr)
{
    foreach ($arr as $k => $v)
        if (empty($v))
            unset($arr[$k]);
    return $arr;
}

function sqlFilter($str)
{
    return str_replace(array('\'', '"', '\\', '%'), '', $str);
}
?>
